/***************************************************************
* gfsLibrary.c : GFS user-library Code
*
* @author : Ridho B - 0606101906
* @version : 1.0
*
***************************************************************/

// INCLUDE ////////////////////////////////////////////////////////////////////////////////////////
// C
#include <stdio.h>
#include <string.h>
#include <pthread.h>
// Globus
#include <globus_common.h>
#include <globus_ftp_client.h>
#include <globus_gsi_cert_utils.h>
#include <globus_gsi_system_config.h>
#include <globus_gsi_proxy.h>
// GFS
#include "gfsLibrary.h"
#include "gfsGlobal.h"
#include "gfsServices.h"

// CONSTANT ///////////////////////////////////////////////////////////////////////////////////////
#define URL_SIZE 300
/*
#define GFS_BUFFER_SIZE 10000
#define GFS_CHUNK_SIZE GFS_BUFFER_SIZE / 10
*/
// STRUCT DATA ////////////////////////////////////////////////////////////////////////////////////
// for globus action**
typedef struct globus_actionD{	
	globus_ftp_client_handle_t 	handle;	
	globus_mutex_t 			lock;
	globus_cond_t 			cond;
	globus_bool_t 			done;
	pthread_t 			thread;
} globus_actionD;
//synonim declaration
typedef globus_actionD *globus_action;

// for Transfer File**
typedef struct thread_dataD{
	int id;
	GFS_file file;
	GFS_physical_path physical_path;
	globus_byte_t *gBuffer;
	globus_size_t  gSize;
	globus_off_t gOffset_local;
	globus_off_t gOffset_gfs;
} thread_dataD;
//synonim declaration
typedef thread_dataD *thread_data;

// for Move and Remove File**
typedef struct thread_data2D{
	int id;
	GFS_file file;
	GFS_physical_path physical_path1;
	GFS_physical_path physical_path2;
} thread_data2D;
//synonim declaration
typedef thread_data2D *thread_data2;



// ANSI function prototype : Private //////////////////////////////////////////////////////////////
// utility
int 		p_getFileSize(FILE *input);
void 		p_getFullPath(char *strLocation, char *strItemName, char *strGFSPath_ref);
void 		p_setErrorParam(char *strParam);
void        	p_splitGFSPath(char *strGFSPath, char *strGFSLoc_ref, char *strItemName_ref);
// GFS-private
void 		*p_gfs_actual_write_file(void *);
void 		*p_gfs_actual_read_file(void *);
void 		*p_gfs_actual_remove_file(void *);
void 		*p_gfs_actual_move_file(void *);
int 		p_gfs_move_file(char *strOldPhysicalPath, char *strNewPhysicalPath);
int 		p_gfs_move_dir(char *strOldPhysicalPath, char *strNewPhysicalPath);
int		p_seek_buffer(globus_byte_t *,  int , int  ,  globus_byte_t *);
// Globus-private
int 		p_pre_globus(globus_action ga);
int 		p_post_globus(globus_action ga);
void 		p_callback_function(void *, globus_ftp_client_handle_t *, globus_object_t *);
void 		p_exist_callback_function(void *, globus_ftp_client_handle_t *, globus_object_t *);
static void p_write_data_callback(void *, globus_ftp_client_handle_t *, globus_object_t *, globus_byte_t *, globus_size_t , globus_off_t, globus_bool_t);
static void p_read_data_callback(void *, globus_ftp_client_handle_t *,globus_object_t *, globus_byte_t *, globus_size_t, globus_off_t, globus_bool_t);

// Global Variable ////////////////////////////////////////////////////////////////////////////////
// use for GFS*
char			strErrorParam_g[MAX_NAME];
int 			GFS_CHUNK_SIZE;
pthread_mutex_t offset_mutex;
//char	*strLocError;
//char	*strItemError;
// use for GLOBUS*
globus_mutex_t 							lock_g;
globus_cond_t 							cond_g;
globus_bool_t 							done_g;
globus_bool_t 							exists_g;
globus_size_t							asc_g;
globus_size_t							size_g;
globus_off_t							local_offset_g;
globus_off_t							gfs_offset_g;
globus_byte_t*							buffer_g;

//			///////////////////////////////////////////////////////////////////////////////  
// 	INITIALIZATION 	///////////////////////////////////////////////////////////////////////////////
//			///////////////////////////////////////////////////////////////////////////////

/**************************************************************************************************
**	INIT GLOBUS PROXY	
**************************************************************************************************/
int gfs_init_GFS(){ 
	//Define Variable
	globus_result_t 			status;
   	time_t 						lifetime;
   	char 						*tmpstr;
   	char 						*proxy_filename;
   	globus_gsi_cred_handle_t    proxy_cred 	= NULL;
   	X509 						*proxy_cert = NULL;
   	EVP_PKEY 					*proxy_pubkey = NULL;

	//STEP 1: globus modul activate----------------------------------------------------------------

    status = globus_module_activate(GLOBUS_GSI_PROXY_MODULE);
    if(status != GLOBUS_SUCCESS){ 
      	tmpstr = globus_object_printable_to_string(globus_error_get(status));
     	globus_libc_printf("\n|X| GFS-init Error: Failed to load GLOBUS_GSI_PROXY_MODULE.\n|X| GFS-init Error Code : %d \n|X| GFS-init Error: %s \n",status,tmpstr);
      	return 0;
    }

   	status = GLOBUS_GSI_SYSCONFIG_GET_PROXY_FILENAME(&proxy_filename,GLOBUS_PROXY_FILE_INPUT);
   	if(status != GLOBUS_SUCCESS){
      	tmpstr = globus_object_printable_to_string(globus_error_get(status));
      	globus_libc_printf("\n|X| GFS-init Error: Couldn't find a valid proxy.\n|X| GFS-init Error Code : %d \n|X| GFS-init Error: %s \n",status,tmpstr);
      	return 0;
    }
  
    status = globus_gsi_cred_handle_init(&proxy_cred, NULL);
    if(status != GLOBUS_SUCCESS){
    	tmpstr = globus_object_printable_to_string(globus_error_get(status));
    	globus_libc_printf("\n|X| GFS-init Error: Failed to initialize proxy credential handle. \n|X| GFS-init Error: Code : %d \n|X| GFS-init Error: %s\n", status,tmpstr);
    	return 0;
    }

   status = globus_gsi_cred_read_proxy(proxy_cred, proxy_filename);
   if(status != GLOBUS_SUCCESS){
      	tmpstr = globus_object_printable_to_string(globus_error_get(status));
      	globus_libc_printf("\n|X| GFS-init Error: Couldn't read proxy from: %s \n|X| GFS-init Error: Error Code : %d \n|X| GFS-init Error: %s\n", tmpstr, status, proxy_filename);
		return 0;
   }

   status = globus_gsi_cred_get_cert(proxy_cred, &proxy_cert);
   if(status != GLOBUS_SUCCESS){
      	globus_libc_printf("\n|X| GFS-init Error: Couldn't get the proxy certificate from the proxy credential.\n");
      	return 0;
   }

   if((proxy_pubkey = X509_get_pubkey(proxy_cert)) == NULL){
      	globus_libc_printf("\n|X| GFS-init Error: Unable to load public key from proxy.\n");
      	globus_module_deactivate_all();
      	return 0;
   }

   //STEP 2: validity: set time_diff to time to expiration (in seconds)----------------------------
   globus_libc_printf("|0| GFS-init : Checking for Valid Grid Proxy ...");
   
   status = globus_gsi_cred_get_lifetime(proxy_cred,&lifetime);
   if(status != GLOBUS_SUCCESS){
      	globus_libc_printf("\n|X| GFS-init Error: Couldn't get a valid lifetime for the proxy credential.\n");
      	return 0;
    }

   if (lifetime <= 0){
       	lifetime = 0;
       	globus_libc_printf("\n|X| GFS-init Error: Proxy Expired. Please run myproxy-logon.\n");
       	return 0;
   }
   globus_libc_printf("\n|0| GFS-init : Found Valid Grid Proxy");
   globus_libc_printf("\n|0| GFS-init : Time Left : %ld:%02ld:%02ld",
                   (long)(lifetime / 3600),
                   (long)(lifetime % 3600) / 60,
                   (long)lifetime % 60 );

   if (lifetime > 3600 * 24)
   		globus_libc_printf("\n|0| GFS-init : (%.1f days)", (float)(lifetime / 3600) / 24.0);
   

   //STEP 3: deactivation and cleanup--------------------------------------------------------------
   globus_module_deactivate_all();
   if(proxy_filename != NULL)
      	free(proxy_filename);      
   return 1;
}
/**************************************************************************************************
**	INIT GFS RNS	
**************************************************************************************************/
int gfs_init_RNS(){
	printf("\n|0| GFS-init : Trying to initialize service ...");
	
	//Define Variable
	GFS_server headPtr = NULL;
	
	//STEP 1: open metadata database connection----------------------------------------------------
  	if (gfs_init_metadata())
		printf("\n|V| GFS-init : Database connection success.");
	else{
		printf("\n|X| GFS-init Error: Database connection failed.\n");
		return 0;
	}
		
	//STEP 2: show available server----------------------------------------------------------------
  	if(gfs_get_all_server(&headPtr)){
  		GFS_server now=NULL;
		if (headPtr != NULL)
		{
			now = headPtr;
			while (now)
			{				
				printf("\n|0| GFS-init-server : %s | %lld/%lld | %s", now->hostname, now->used, now->total, now->online);
				now = now->next;
			}
		}
  	}
  	else{
		printf("\n|X| GFS-init Error: No Server available for this time.\n");
		return 0;
  	} 			
	return GFS_SUCCESS;
} 

//					//  
// 	ACCESS FILE 	//
//					//

/**************************************************************************************************
**	OPEN FILE	
**************************************************************************************************/
int gfs_open_file(char *strGFSPath, char *mode, GFS_file *file_refer){
	verbose("L:gfs_open_file() : Params(gfs-path = %s, mode = %s)\n", strGFSPath, mode);	
	//Define Variable
	int iStatus;
	GFS_file file_ref = malloc(sizeof(GFS_fileD));
	//file_ref->physical_path = malloc(sizeof(GFS_physical_pathD));
	strcpy(file_ref->mode, mode);

	//STEP 1: get GFSLoc and item name-------------------------------------------------------------
	verbose("L:gfs_open_file() : get GFSLoc and item name\n");	
	p_splitGFSPath(strGFSPath, file_ref->logical_loc, file_ref->file_name);
    	p_setErrorParam(file_ref->file_name);

	//STEP 2: lock metadata -----------------------------------------------------------------------    
    	verbose("L:gfs_open_file() : lock metadata\n");
    	if(!strcmp(mode,"w"))
        	iStatus = gfs_lockMetadata("x", "wd", 1, file_ref->file_name, file_ref->logical_loc);
    	else if(!strcmp(mode,"w+"))
        	iStatus = gfs_lockMetadata("x", "wf", 0, file_ref->file_name, file_ref->logical_loc);
    	else if(!strcmp(mode,"r"))
        	iStatus = gfs_lockMetadata("s", "rf", 0, file_ref->file_name, file_ref->logical_loc);
    	if(iStatus <= GFS_FAILED)
        	return iStatus;
    	file_ref->pid = iStatus;
    
    	//STEP 3: get physical location ---------------------------------------------------------------    
    	verbose("L:gfs_open_file() : get physical location\n");
    	iStatus = gfs_getFileLocation(file_ref);
    	if(!strcmp(mode,"w+")) // set offset
    		file_ref->offset = file_ref->file_size;
    	else // set offset
    		file_ref->offset = 0;
    	if(!strcmp(mode,"w")) // set size
    		file_ref->file_size = 0;
    	asc_g = 0;

    	//STEP 4: Check open status -------------------------------------------------------------------
	verbose("L:gfs_open_file() : check open status\n");
	if(iStatus == GFS_FAILED){
        if(!strcmp(mode,"w"))
            gfs_unlockMetadata(file_ref->pid, 1, file_ref->file_name, file_ref->logical_loc);
        else if(!strcmp(mode,"w+"))
            gfs_unlockMetadata(file_ref->pid, 0, file_ref->file_name, file_ref->logical_loc);
        else if(!strcmp(mode,"r"))
            gfs_unlockMetadata(file_ref->pid, 0, file_ref->file_name, file_ref->logical_loc);
        return iStatus;
	}

    	//STEP 5: Globus  modul activated ---------------------------------------------------------
    	verbose("L:gfs_open_file() : globus modul activated \n");
	int iResult = globus_module_activate(GLOBUS_FTP_CLIENT_MODULE);
	if (iResult != GLOBUS_SUCCESS) {
		verbose("L:gfs_open_file()--|X| Failed to load the Globus FTP Client Module \n");
		return ECONN;
	}	
/*
    	verbose("L:gfs_open_file() : globus transfer preparation \n");
    	file_ref->process_status = GFS_FAILED;
    	GFS_physical_path now = NULL;
	if (file_ref->physical_path != NULL) {
		now = file_ref->physical_path;
		while (now){
	    	if(p_pre_globus(now) != GFS_SUCCESS)
				return ECONN;
			now=now->next;
		}
	}
*/

	//STEP 6: Return if all step above done--------------------------------------------------------
	if(VERBOSE){
		verbose("L:gfs_open_file() : Process-params(file-name = %s, physical-location: ", file_ref->file_name);
    		GFS_physical_path now = NULL;
		if (file_ref->physical_path != NULL) {
			now = file_ref->physical_path;
			while (now){
				verbose("\n\t %s ", now->url);
				now=now->next;
			}			
			verbose(")\n");
		}
	}
	*file_refer = file_ref;
	return GFS_SUCCESS;
}
/**************************************************************************************************
**	CLOSE FILE	
**************************************************************************************************/
int gfs_close_file(GFS_file *file){
	verbose("L:gfs_close_file() : Params(GFS_file handler: %s, process_status: %d)\n", (*file)->file_name, (*file)->process_status );	
	//Define Variable
	int iStatus;
    
    	//STEP 1: globus deactivation ------------------------------------------------------	    
    	verbose("L:gfs_close_file() : globus deactivation\n");	
    	globus_module_deactivate(GLOBUS_FTP_CLIENT_MODULE);

	//STEP 0: check file handler-------------------------------------------------------------------	
	verbose("L:gfs_close_file() : check file handler\n");	
	if((*file) == NULL) return GFS_SUCCESS; // nothing to close

	//STEP 2: save Metadata------------------------------------------------------------------------	
	verbose("L:gfs_close_file() : save Metadata\n");
	if((*file)->process_status == GFS_SUCCESS  && ((!strcmp((*file)->mode, "w") || !strcmp((*file)->mode, "w+"))))
		iStatus = gfs_saveFileMetadata(*file);
    
    	//STEP 3: unlock Metadata------------------------------------------------------------------------	
	verbose("L:gfs_close_file() : unlock Metadata\n");	
    	if(!strcmp((*file)->mode,"w")){
    	if((*file)->process_status == GFS_SUCCESS )
       	 	gfs_unlockMetadata((*file)->pid, 0, (*file)->file_name, (*file)->logical_loc);
       	else       		
       	 	gfs_unlockMetadata((*file)->pid, 1, (*file)->file_name, (*file)->logical_loc);
    	}
    	else if(!strcmp((*file)->mode,"w+"))
        	gfs_unlockMetadata((*file)->pid, 0, (*file)->file_name, (*file)->logical_loc);
    	else if(!strcmp((*file)->mode,"r"))
        	gfs_unlockMetadata((*file)->pid, 0, (*file)->file_name, (*file)->logical_loc);
    
	//STEP 4: check close status -------------------------------------------------------------------
	verbose("L:gfs_open_file() : check close status\n");
	if(iStatus == GFS_FAILED){
		p_setErrorParam((*file)->file_name);
        	return iStatus;
    	}
        
	//STEP 5: return if all step above done -------------------------------------------------------
	*file=NULL;
	return GFS_SUCCESS;
}
/**************************************************************************************************
**	END FILE
**************************************************************************************************/
int gfs_end_file(GFS_file file){
	
	//STEP 0: check end file ----------------------------------------------------------------------
	verbose("L:gfs_end_file() : check end file \n");
	if(file->offset >= file->file_size){
        	return GFS_SUCCESS;
    	}
    	else 
    		return GFS_FAILED;
}
/**************************************************************************************************
**	WRITE FILE
**************************************************************************************************/
int gfs_write_file(GFS_file file, void *buffer, int iSize){	
	if(VERBOSE)printf("L:gfs_write_file() : Params(file = %s | size = %d)\n", file->file_name, iSize);	
	         
	//STEP 0: check file handler-------------------------------------------------------------------	
	verbose("L:gfs_write_file() : check file handler\n");	
	if(file == NULL) return GFS_SUCCESS; // nothing to write
    	file->process_status = GFS_FAILED;
	
	//STEP 1: write file to location --------------------------------------------------------------
	GFS_physical_path now = NULL;
	if (file->physical_path != NULL) {
		// create thread*
		now = file->physical_path;
		int iThread = 1;
		while (now)	{
			// fill thread data*
			thread_data thd = malloc(sizeof(thread_dataD));
			thd->id = iThread;
			thd->file = file;
			thd->physical_path = now;
			thd->gBuffer = (globus_byte_t *)buffer;
			thd->gSize = (globus_size_t)iSize;
			// create thread per location*
			if(pthread_create(&now->thread, NULL, p_gfs_actual_write_file, thd)){
				file->process_status = ECONN;
				return ECONN;
			}
			// next*
			now=now->next;
			iThread = iThread +1;
		}

		// wait each thread*
		now = file->physical_path;
		while (now)	{
			pthread_join(now->thread, NULL);
			if(file->process_status != GFS_SUCCESS){
     				return file->process_status;
			}
			// next*
			now=now->next;
		}

	}
	//STEP 3: update file data --------------------------------------------------------------------
	verbose("L:gfs_write_file() : update file offset: ofset + %d\n", iSize);
	file->offset = file->offset + iSize;
	file->file_size = file->file_size + iSize;

	//STEP 4: done if all step above success-------------------------------------------------------
    	file->process_status = GFS_SUCCESS;
	return GFS_SUCCESS;
}
void *p_gfs_actual_write_file(void *data){		
	//Define Variable
	thread_data thd = (thread_data)data;
    	int result;   				// Result Flag
    	char gsi_dst[URL_SIZE] = "gsiftp://";	// to-location
	globus_action	ga = malloc(sizeof(globus_actionD));	// for globus action
	strcat(gsi_dst, thd->physical_path->url);
	if(VERBOSE)printf("%d*L:gfs_actual_write_file() : Params(file = %s | offset= %lld | size = %d | physical-location = %s)\n", thd->id, thd->file->file_name, thd->file->offset, thd->gSize, thd->physical_path->url);	
	
	//BASE 0: Globus prepare -----------------------------------------------------------------
	verbose("%d*L:gfs_write_file() : globus gridftp handle initiated \n", thd->id);
	if(p_pre_globus(ga) != GFS_SUCCESS){
		thd->file->process_status = ECONN;
		return (void *)ECONN;
	}

	/********************************************************************
 	* STEP 5.a: Execute the operation; globus_ftp_client_put starts the
 	* protocol exchange on the control channel.  Note that this does NOT
 	* start moving data over the data channel
 	*******************************************************************/
    	verbose("%d*L:gfs_actual_write_file() : open connection and transfer file\n", thd->id);	
    	ga->done = GLOBUS_FALSE;
    	result = globus_ftp_client_partial_put(&ga->handle,
				   gsi_dst,
				   GLOBUS_NULL,
				   GLOBUS_NULL,   /* no restart markers */
				   (GFS_offset)(thd->file->offset), 
				   (GFS_offset)(thd->file->offset + thd->gSize),
				   p_callback_function,
				   (void *)ga);  
	if (result != GLOBUS_SUCCESS) {
     	verbose("%d*L:gfs_actual_write_file() : |X| %s\n",thd->id, globus_object_printable_to_string(globus_error_get(result)));
     	ga->done = GLOBUS_TRUE;
     	thd->file->process_status = ECONN;
        return (void *)ECONN;
	}
     	/**************************************************************
	 * STEP 5.b: This is where the data movement over the data channel 
	 * is initiated. You read a buffer, split to amount of parallelism 
	 * and call register_write. This is an asynchcall which returns 
	 * immediately.  When it is finished writing the buffer, 
	 * it calls the data callback (defined above) which reads another 
	 * buffer and calls register_write again.
	 * The data callback will also indicate when you have hit eof
	 * Note that eof on the data channel does not mean the control
	 * channel protocol exchange is complete.  This is indicated by
	 * the done callback being called.
	 *************************************************************/
    	verbose("%d*L:gfs_actual_write_file() : try to transfer data\n",thd->id);
    	globus_ftp_client_register_write(&ga->handle, thd->gBuffer, thd->gSize, thd->file->offset, GLOBUS_TRUE, p_write_data_callback, (void *)thd->physical_path);
    	/*********************************************************************
	* The following is a standard thread construct.  The while loop is
 	* required because pthreads may wake up arbitrarily.  In non-threaded
 	* code, cond_wait becomes globus_poll and it sits in a loop using
 	* CPU to wait for the callback.  In a threaded build, cond_wait would
 	* put the thread to sleep
 	*********************************************************************/
    	globus_mutex_lock(&ga->lock);
    	while(!ga->done){
           globus_cond_wait(&ga->cond, &ga->lock);
    	}
    	globus_mutex_unlock(&ga->lock);   

	//STEP 1: globus deactivation and cleanup------------------------------------------------------	    
    	verbose("%d*L:gfs_write_file() : globus gridftp handle destroy\n", thd->id);	
    	if(!p_post_globus(ga)){
		thd->file->process_status = ECONN;
		return (void *)ECONN;
	}

	//STEP 4: Return if all step above success-----------------------------------------------------
	verbose("%d*L:gfs_actual_write_file() : success transfer part of file\n", thd->id); 
	thd->file->process_status = GFS_SUCCESS;	

	return (void *)GFS_SUCCESS;
}
/**************************************************************************************************
**	READ FILE
**************************************************************************************************/
int gfs_read_file(GFS_file file, void *buffer, int iSize){	
	if(VERBOSE)printf("L:gfs_read_file() : Params(file = %s | size = %d)\n", file->file_name, iSize);	
	
	//STEP 0: check file handler-------------------------------------------------------------------	
	verbose("L:gfs_read_file() : check file handler\n");	
	if(file == NULL) return GFS_SUCCESS; // nothing to read
    	file->process_status = GFS_FAILED;
		
	
	//STEP 1: read file from location -------------------------------------------------------------
	GFS_physical_path now = NULL;
	if (file->physical_path != NULL) {
		// variable for operation*
		GFS_CHUNK_SIZE = ((iSize +1)/CHUNKS);
		if(GFS_CHUNK_SIZE == 0) GFS_CHUNK_SIZE = iSize;
		// create thread*
		local_offset_g = 0;
		gfs_offset_g = file->offset;
		now = file->physical_path;
		int iThread = 1;
		while (now){
			// update offset*
			if(iThread > 1){
				pthread_mutex_lock (&offset_mutex);			
				local_offset_g = local_offset_g + GFS_CHUNK_SIZE;
				gfs_offset_g = gfs_offset_g + GFS_CHUNK_SIZE;
				pthread_mutex_unlock (&offset_mutex);
			}
			// fill thread data*
			thread_data thd = malloc(sizeof(thread_dataD));
			thd->id = iThread;
			thd->file = file;
			thd->physical_path = now;
			thd->gBuffer =(globus_byte_t *)buffer;
			thd->gSize = (globus_size_t)iSize;
			thd->gOffset_local = local_offset_g;
			thd->gOffset_gfs = gfs_offset_g;
			// create thread per location*
			if(pthread_create(&now->thread, NULL, p_gfs_actual_read_file, thd)){
				file->process_status = ECONN;
				return ECONN;
			}
			// next*
			now=now->next;
			iThread = iThread + 1;
		}

		// wait each thread*
		now = file->physical_path;
		while (now)	{
			pthread_join(now->thread, NULL);
			if(file->process_status != GFS_SUCCESS){
     				return file->process_status;
			}
			// next*
			now=now->next;
		}

	}
	
	//STEP 2: update file data --------------------------------------------------------------------
	verbose("L:gfs_read_file() : update file offset: ofset + %d\n", iSize);
	file->offset = file->offset + iSize;
	
	//STEP 3: done if all step above success-------------------------------------------------------
    	file->process_status = GFS_SUCCESS;
    	if(file->file_size > file->offset)// Return size of file buffer
    		return iSize;
   	else
    		return file->file_size - (file->offset - iSize);
}
void *p_gfs_actual_read_file(void *data){
	//Define Variable
	thread_data thd = (thread_data)data;
    	int result;  				// Result Flag
	int CHUNK_SIZE;				// Chunks
    	char gsi_src[URL_SIZE] = "gsiftp://";	// from-location
	globus_action	ga = malloc(sizeof(globus_actionD));	// for globus action
	strcat(gsi_src, thd->physical_path->url);
	pthread_mutex_lock (&offset_mutex);
	if((thd->gSize - local_offset_g) > GFS_CHUNK_SIZE)
		CHUNK_SIZE = GFS_CHUNK_SIZE;
	else
		CHUNK_SIZE = (thd->gSize - local_offset_g);
	pthread_mutex_unlock (&offset_mutex);
	if(VERBOSE)
		printf("%d*L:gfs_actual_read_file() : Params(file = %s | gfs-offset = %d/%d | chunk-size = %d | physical-location = %s)\n", thd->id, thd->file->file_name, (int)thd->gOffset_gfs, (int)local_offset_g, (int)CHUNK_SIZE, thd->physical_path->url);	
	
	//STEP 0: Globus prepare -----------------------------------------------------------------
	verbose("%d*L:gfs_read_file() : globus gridftp handle initiated \n", thd->id);
	if(p_pre_globus(ga) != GFS_SUCCESS){
		thd->file->process_status = ECONN;
		return (void *)ECONN;
	}

	//BASE 0: Buffer already full -----------------------------------------------------------------
	if(local_offset_g >= thd->gSize){
		verbose("%d*L:gfs_actual_read_file() : buffer has full\n", thd->id);
		thd->file->process_status = GFS_SUCCESS;
		return (void *)GFS_SUCCESS;
	}
	
	/********************************************************************
 	* STEP 5.a: Execute the operation; globus_ftp_client_get starts the
 	* protocol exchange on the control channel.  Note that this does NOT
 	* start moving data over the data channel
 	*******************************************************************/
    	verbose("%d*L:gfs_actual_read_file() : open connection and transfer file\n", thd->id);	
    	// globus_ftp_client_put starts the protocol exchange on the control channel
   	// Note : This does NOT start moving data over the data channel
    	ga->done = GLOBUS_FALSE;
    	result = globus_ftp_client_partial_get(&ga->handle,
				   gsi_src,
				   GLOBUS_NULL,
				   GLOBUS_NULL,   /* no restart markers */
				   thd->gOffset_gfs, 
				   thd->gOffset_gfs + CHUNK_SIZE,
				   p_callback_function,
				   (void *)ga);  
	if (result != GLOBUS_SUCCESS) {
	     	verbose("%d*L:gfs_actual_read_file() : |X| %s\n", thd->id, globus_object_printable_to_string(globus_error_get(result)));
	     	ga->done = GLOBUS_TRUE;
	     	thd->file->process_status = ECONN;
	     	return (void *)ECONN;
	}
     	/**************************************************************
	 * STEP 5.b: This is where the data movement over the data channel 
	 * is initiated. You read a buffer, split to amount of parallelism 
	 * and call register_write. This is an asynchcall which returns 
	 * immediately.  When it is finished writing the buffer, 
	 * it calls the data callback (defined above) which reads another 
	 * buffer and calls register_write again.
	 * The data callback will also indicate when you have hit eof
	 * Note that eof on the data channel does not mean the control
	 * channel protocol exchange is complete.  This is indicated by
	 * the done callback being called.
	 *************************************************************/
    	verbose("%d*L:gfs_actual_read_file() : try to transfer data\n", thd->id);    
	globus_ftp_client_register_read(&ga->handle, (thd->gBuffer + thd->gOffset_local), CHUNK_SIZE, p_read_data_callback, (void *)thd->physical_path);
    	/*********************************************************************
	* The following is a standard thread construct.  The while loop is
 	* required because pthreads may wake up arbitrarily.  In non-threaded
 	* code, cond_wait becomes globus_poll and it sits in a loop using
 	* CPU to wait for the callback.  In a threaded build, cond_wait would
 	* put the thread to sleep
 	*********************************************************************/
    	globus_mutex_lock(&ga->lock);
    	while(!ga->done) {
           globus_cond_wait(&ga->cond, &ga->lock);
    	}
    	globus_mutex_unlock(&ga->lock);
	
	//STEP 1: globus deactivation and cleanup------------------------------------------------------	      
    	verbose("%d*L:gfs_read_file() : globus gridftp handle destroy\n", thd->id);	
    	if(!p_post_globus(ga)){
		thd->file->process_status = ECONN;
		return (void *)ECONN;
	}
   	
   	//STEP 5: Recursive for next chunks------------------------------------------------------------
	verbose("%d*L:gfs_actual_read_file() : success transfer part of file\n", thd->id);
	pthread_mutex_lock (&offset_mutex);
	// set offset for another thread
	local_offset_g = local_offset_g + CHUNK_SIZE;
	gfs_offset_g = gfs_offset_g + CHUNK_SIZE;			
	// set offset for this thread		
	thd->gOffset_local = local_offset_g;
	thd->gOffset_gfs = gfs_offset_g;
	pthread_mutex_unlock (&offset_mutex); 	
	// recursive
	p_gfs_actual_read_file(thd);
}
/**************************************************************************************************
**	REMOVE FILE
**************************************************************************************************/
int gfs_remove_file(char *strGFSPath){	
	verbose("L:gfs_remove_file() : Params( gfs-path = %s)\n",  strGFSPath);
	//define variable
	GFS_file file = malloc(sizeof(GFS_fileD));
	int iStatus;
	
	//STEP 0: build file handler ------------------------------------------------------------------
	verbose("L:gfs_remove_file() : build file handler\n");
	p_splitGFSPath(strGFSPath, file->logical_loc, file->file_name);
	strcpy(file->mode, "d"); // delete mode
    	p_setErrorParam(file->file_name);
	
	//STEP 1: lock metadata -----------------------------------------------------------------------
	verbose("L:gfs_remove_file() : lock metadata \n");
    	iStatus = gfs_lockMetadata("x", "wd", 0, file->file_name, file->logical_loc);    
		if(iStatus <= GFS_FAILED)
        return iStatus;    
    	file->pid = iStatus;
    
    	//STEP 2: get physical location ---------------------------------------------------------------    
    	verbose("L:gfs_remove_file() : get physical location\n");
    	iStatus = gfs_getFileLocation(file);    
    	if(iStatus != GFS_FAILED){
        
    		//STEP 5: Globus  modul activated ---------------------------------------------------------
    		verbose("L:gfs_remove_file() : globus modul activated \n");
		int iResult = globus_module_activate(GLOBUS_FTP_CLIENT_MODULE);
		if (iResult != GLOBUS_SUCCESS) {
			verbose("L:pre_globus()--|X| Failed to load the Globus FTP Client Module \n");
			return ECONN;
		}

        	//STEP 3: delete file from location(s) ----------------------------------------------------
        	GFS_physical_path now = NULL;
		if (file->physical_path != NULL) {
			// create thread*
			now = file->physical_path;
			int iThread = 1;
			while (now)	{
				verbose("L:gfs_delete_file() : remove file in location: %s\n", now->url);
				// fill thread data*
				thread_data2 thd = malloc(sizeof(thread_data2D));
				thd->id = iThread;
				thd->file = file;
				thd->physical_path1 = now;
				// create thread per location*
				if(pthread_create(&now->thread, NULL, p_gfs_actual_remove_file, thd)){
					file->process_status = ECONN;
					return ECONN;
				}
				// next*
				now=now->next;
				iThread = iThread +1;
			}
			// wait each thread*
			now = file->physical_path;
			while (now)	{
				pthread_join(now->thread, NULL);
				if(file->process_status != GFS_SUCCESS){
	     				return file->process_status;
				}
				// next*
				now=now->next;
			}
		}	
    	}
    	if(iStatus != GFS_FAILED){
            
    	//STEP 1: globus deactivation ------------------------------------------------------	    
    	verbose("L:gfs_remove_file() : globus deactivation\n");	
    	globus_module_deactivate(GLOBUS_FTP_CLIENT_MODULE);

        //STEP 4: delete file from metadata -------------------------------------------------------
        verbose("L:gfs_remove_file() : save metadata\n");
        iStatus = gfs_saveFileMetadata(file);
	
    	}    
    	//STEP 5: unlock Metadata----------------------------------------------------------------------
	verbose("L:gfs_remove_file() : unlock Metadata\n");	
    	gfs_unlockMetadata(file->pid, 0, file->file_name, file->logical_loc);
           
	//STEP 6: done if all step above done----------------------------------------------------------
	return iStatus;
}
void *p_gfs_actual_remove_file(void *data){	
    	//Define Variable
    	thread_data2 thd = (thread_data2)data;
	GFS_physical_path physical_path = thd->physical_path1;
	globus_action	ga = malloc(sizeof(globus_actionD));	// for globus action
    	int 		result;  		// Result Flag
    	verbose("%d*L:p_gfs_actual_remove_file() : Params( gfs-physical-path = %s)\n", thd->id,  physical_path->url);
	        
  	//STEP 1: Globus Preparation-----------------------------------------------------------------  	
	verbose("%d*L:p_gfs_actual_remove_file() : globus Preparation\n", thd->id);	
	char url_loc[URL_SIZE] = "gsiftp://";
	strcat(url_loc, physical_path->url);
	if(p_pre_globus(ga) != GFS_SUCCESS){
		thd->file->process_status = ECONN;
		return (void *)ECONN;
	}

	//STEP 2: execute the operation----------------------------------------------------------------
    	verbose("%d*L:p_gfs_actual_remove_file() : delete physical file\n", thd->id);	
	/* globus_ftp_client_delete starts the protocol for delete file
   	 */
    	ga->done = GLOBUS_FALSE;
    	result = globus_ftp_client_delete (&ga->handle, url_loc, GLOBUS_NULL, p_callback_function, (void *)ga);
    	if (result != GLOBUS_SUCCESS) {
	     	verbose("%d*L:p_gfs_actual_remove_file(): |X| %s\n", thd->id, globus_object_printable_to_string(globus_error_get(result)));
	     	ga->done = GLOBUS_TRUE;
	     	thd->file->process_status = ECONN;
		return (void *)ECONN;
    	}
    	/* Lock on condition until the function is completed */
    	globus_mutex_lock(&ga->lock);
    	while(!ga->done) {
		   /* Atomically release mutex and wait on cond. When the function returns,
		    * mutex has been reacquired */
		   globus_cond_wait(&ga->cond, &ga->lock);
    	}
    	/* Unlock the mutual exclusion lock, mutex, enabling another thread to acquire the mutex */
    	globus_mutex_unlock(&ga->lock);    

	///STEP 3: deactivation and cleanup------------------------------------------------------------   
    	verbose("%d*L:p_gfs_actual_remove_file() : globus post execution\n", thd->id);	
    	if(p_post_globus(ga) != GFS_SUCCESS){
    		thd->file->process_status = ECONN;
    		return (void *)ECONN;	
    	}
    
	//STEP 4: Return if all step above success-----------------------------------------------------
	verbose("%d*L:p_gfs_actual_remove_file() : success delete file on this location\n", thd->id); 
	thd->file->process_status = GFS_SUCCESS;
	return (void *)GFS_SUCCESS;
}

/**************************************************************************************************
**	MOVE FILE
**************************************************************************************************/
int p_gfs_move_file(char *strSrcGFSPath, char *strDstGFSPath){	
	verbose("L:gfs_move_file() : Params( src-gfs-path = %s, dst-gfs-path = %s)\n",  strSrcGFSPath, strDstGFSPath);
	//define variable
	GFS_file src_file = malloc(sizeof(GFS_fileD));
	GFS_file dst_file = malloc(sizeof(GFS_fileD));
	int iStatus;
	
	//STEP 0: build file handler ------------------------------------------------------------------
	verbose("L:gfs_move_file() : build file handler\n");
	p_splitGFSPath(strSrcGFSPath, src_file->logical_loc, src_file->file_name);
	strcpy(src_file->mode, "d"); // delete mode
	p_splitGFSPath(strDstGFSPath, dst_file->logical_loc, dst_file->file_name);
	strcpy(dst_file->mode, "w"); // write mode
    	
	//STEP 1: lock metadata -----------------------------------------------------------------------
	verbose("L:gfs_move_file() : lock metadata \n");
    	iStatus = gfs_lockMetadata("x", NULL, 0, src_file->file_name, src_file->logical_loc);    
		if(iStatus <= GFS_FAILED){
        	p_setErrorParam(src_file->file_name);
        	return iStatus;
    	}
    	src_file->pid = iStatus;
    	iStatus = gfs_lockMetadata("x", NULL, 1, dst_file->file_name, dst_file->logical_loc);    
	if(iStatus <= GFS_FAILED){
        	p_setErrorParam(dst_file->file_name);
        	return iStatus;
    	}
    	dst_file->pid = iStatus;
    
    	//STEP 2: get physical location ---------------------------------------------------------------    
    	verbose("L:gfs_move_file() : get physical location\n");
    	iStatus = gfs_getFileLocationMove(src_file, dst_file);    
    	if(iStatus != GFS_FAILED){
        
    		//STEP 5: Globus  modul activated ---------------------------------------------------------
    		verbose("L:gfs_remove_file() : globus modul activated \n");
		int iResult = globus_module_activate(GLOBUS_FTP_CLIENT_MODULE);
		if (iResult != GLOBUS_SUCCESS) {
			verbose("L:pre_globus()--|X| Failed to load the Globus FTP Client Module \n");
			return ECONN;
		}

        	//STEP 3: move file from location(s) ------------------------------------------------------
        	GFS_physical_path src_now = NULL;
        	GFS_physical_path dst_now = NULL;
		if (src_file->physical_path != NULL && dst_file->physical_path != NULL) {
			// create thread*
			src_now =src_file->physical_path;
			dst_now =dst_file->physical_path;
			int iThread = 1;
			while (src_now != NULL && dst_now != NULL)	{
				verbose("L:gfs_delete_file() : remove file from %s to %s \n", src_now->url, dst_now->url);
				// fill thread data*
				thread_data2 thd = malloc(sizeof(thread_data2D));
				thd->id = iThread;
				thd->file = src_file;
				thd->physical_path1 = src_now;
				thd->physical_path2 = dst_now;
				// create thread per location*
				if(pthread_create(&src_now->thread, NULL, p_gfs_actual_move_file, thd)){
					src_file->process_status = ECONN;
					return ECONN;
				}
				// next*
				src_now=src_now->next;
				dst_now=dst_now->next;
				iThread = iThread +1;
			}
			// wait each thread*
			src_now=src_file->physical_path;
			dst_now=dst_file->physical_path;
			while (src_now != NULL && dst_now != NULL)	{
				pthread_join(src_now->thread, NULL);
				if(src_file->process_status != GFS_SUCCESS){
	     				return src_file->process_status;
				}
				// next*
				src_now=src_now->next;
				dst_now=dst_now->next;
			}
		}
   	 }
    	if(iStatus != GFS_FAILED){        
    
    	//STEP 1: globus deactivation ------------------------------------------------------	    
    	verbose("L:gfs_move_file() : globus deactivation\n");	
    	globus_module_deactivate(GLOBUS_FTP_CLIENT_MODULE);

        //STEP 4: move metadata ------------------------------------------------------
        verbose("L:gfs_move_file() : move metadata\n");
        iStatus = gfs_moveFileMetadata(src_file, dst_file);
	
    	}
    	//STEP 5: unlock Metadata----------------------------------------------------------------------
	verbose("L:gfs_move_file() : unlock Metadata\n");
    	gfs_unlockMetadata(src_file->pid, 0, src_file->file_name, src_file->logical_loc);
    	if(iStatus <= GFS_FAILED)
    		gfs_unlockMetadata(dst_file->pid, 1, dst_file->file_name, dst_file->logical_loc);
    	else
    		gfs_unlockMetadata(dst_file->pid, 0, dst_file->file_name, dst_file->logical_loc);
           
	//STEP 6: done if all step above done----------------------------------------------------------
	return iStatus;    
}
void  *p_gfs_actual_move_file(void *data){
	
    	//Define Variable
    	thread_data2 thd = (thread_data2)data;
    	GFS_physical_path physical_path_from = thd->physical_path1;
    	GFS_physical_path physical_path_to = thd->physical_path2;
	globus_action	ga = malloc(sizeof(globus_actionD));	// for globus action
    	int 		result;  		// Result Flag
    	verbose("%d*L:gfs_move_file() : Params( old-gfs-physical-path = %s,  new-gfs-physical-path = %s)\n", thd->id,  physical_path_from->url, physical_path_to->url);
              
  	//STEP 1: Globus Preparation-------------------------------------------------------------------
	verbose("%d*L:gfs_actual_move_file() : globus Preparation\n", thd->id);	
	char gsi_src[URL_SIZE] = "gsiftp://";
	strcat(gsi_src, physical_path_from->url);
	char gsi_dst[URL_SIZE] = "gsiftp://";
	strcat(gsi_dst, physical_path_to->url);
	if(p_pre_globus(ga) != GFS_SUCCESS){
		thd->file->process_status = ECONN;
		return (void *)ECONN;
	}

	//STEP 2: execute the operation----------------------------------------------------------------
    	verbose("%d*L:gfs_actual_move_file() : rename physical file\n", thd->id);	
	/* globus_ftp_client_delete starts the protocol for delete file
   	 */
    	ga->done = GLOBUS_FALSE;
    	result = globus_ftp_client_move(&ga->handle, gsi_src, gsi_dst, GLOBUS_NULL, p_callback_function, (void *)ga);
    	if (result != GLOBUS_SUCCESS) {
		 globus_object_t * err_str;
		 
		 err_str = globus_error_get(result);
		 verbose("%d*L:gfs_actual_move_file() : |X| %s\n", thd->id, globus_object_printable_to_string(err_str));
		 free(err_str);
		 ga->done = GLOBUS_TRUE;
		 thd->file->process_status = ECONN;
		 return (void *)ECONN;
    	}
    	/* Lock on condition until the function is completed */
    	globus_mutex_lock(&ga->lock);
    	while(!ga->done) {
           /* Atomically release mutex and wait on cond. When the function returns,
            * mutex has been reacquired */
           globus_cond_wait(&ga->cond, &ga->lock);
    	}
    	/* Unlock the mutual exclusion lock, mutex, enabling another thread to acquire the mutex */
    	globus_mutex_unlock(&ga->lock);    

	///STEP 3: deactivation and cleanup------------------------------------------------------------   
    	verbose("%d*L:gfs_actual_move_file() : globus post execution\n", thd->id);	
    	if(p_post_globus(ga) != GFS_SUCCESS){
	    	thd->file->process_status = ECONN;
	    	return (void *)ECONN;
    	}
    
    	//STEP 4: Return if all step above success-----------------------------------------------------
	verbose("%d*L:gfs_actual_move_file() : success move file\n", thd->id); 
	thd->file->process_status = GFS_SUCCESS;
	return (void *)GFS_SUCCESS;
}



//					//
// ACCESS DIRECTORY //
//					//
/**************************************************************************************************
**	MAKE DIRECTORY
**************************************************************************************************/
int gfs_create_dir(char *strGFSPath){
	verbose("L:gfs_create_dir() : Params( gfs-path = %s)\n",  strGFSPath);
	//Define variable
	int iStatus;
	GFS_dir dir = malloc(sizeof(GFS_dirD));
	GFS_dir dir_loc = malloc(sizeof(GFS_dirD));
	
	//STEP 0: build dir handler ------------------------------------------------------------------
	verbose("L:gfs_create_dir() : build dir handler\n");
	p_splitGFSPath(strGFSPath, dir->logical_loc, dir->dir_name);
	p_splitGFSPath(dir->logical_loc, dir_loc->logical_loc, dir_loc->dir_name);
	p_setErrorParam( dir->dir_name);
	
	//STEP 1: lock metadata------------------------------------------------------------------------
	verbose("L:gfs_create_dir() : lock metadata\n");    
    iStatus = gfs_lockMetadata("xd", "wd", 1, dir->dir_name, dir->logical_loc);   
	if(iStatus <= GFS_FAILED)
		return iStatus;
	dir->pid = iStatus;
	
	//STEP 3: save matadata ----------------------------------------------------------------------
	verbose("L:gfs_create_dir() : save metadata\n");
	strcpy(dir->mode, "w"); //write mode
	iStatus = gfs_saveDirMetadata(dir);
	dir->process_status = iStatus;
    
    //STEP 4: unlock matadata ----------------------------------------------------------------------
	verbose("L:gfs_create_dir() : unlock metadata\n");
	if(dir->process_status == GFS_SUCCESS)
    	gfs_unlockMetadata(dir->pid, 0, dir->dir_name, dir->logical_loc);
    else		
    	gfs_unlockMetadata(dir->pid, 1, dir->dir_name, dir->logical_loc);
    	
	//STEP 5: Return if all step above success-----------------------------------------------------
	return iStatus;	
}
/**************************************************************************************************
**	READ DIRECTORY
**************************************************************************************************/
int gfs_read_dir(char *strGFSPath, GFS_item *listGFSItem_ref){
	verbose("L:gfs_read_dir() : Params( gfs-path = %s)\n",  strGFSPath);
    //Define variable
	int iStatus;
	GFS_dir dir = malloc(sizeof(GFS_dirD));
    
    //STEP 0: build dir handler ------------------------------------------------------------------
	verbose("L:gfs_read_dir() : build dir handler\n");
	p_splitGFSPath(strGFSPath, dir->logical_loc, dir->dir_name);
    	p_setErrorParam(dir->dir_name);
    	
	//STEP 0: Check permission---------------------------------------------------------------------
	verbose("L:gfs_read_dir() : check permission\n");
	if(gfs_get_permission_location(strGFSPath, "r") != GFS_SUCCESS)
		return EPERMIT;

	//STEP 1: Get Item List and Check NUMBER of FILE in DIRECTORY----------------------------------
	verbose("L:gfs_read_dir() : get file list\n");	
	iStatus = gfs_getItemList(dir, listGFSItem_ref);
	if(iStatus < GFS_FAILED)	
		return EDNOTVALID;		
    	
	//STEP 2: Return if all step above success-----------------------------------------------------
	verbose("L:gfs_read_dir() : Process-params(#dir = %d)\n", iStatus);
	return GFS_SUCCESS;
}
/**************************************************************************************************
**	CHECK DIRECTORY
**************************************************************************************************/
int gfs_check_dir(char *strGFSPath){
	verbose("L:gfs_check_dir() : Params( gfs-path = %s)\n",  strGFSPath);    
    //Define variable
	GFS_dir dir = malloc(sizeof(GFS_dirD));
    
    //STEP 0: build dir handler ------------------------------------------------------------------
	verbose("L:gfs_check_dir() : build dir handler\n");
	p_splitGFSPath(strGFSPath, dir->logical_loc, dir->dir_name);
    p_setErrorParam(dir->dir_name);
    
	//STEP 1: Return isDirExist from service-------------------------------------------------------
	verbose("L:gfs_check_dir() : return isDirExist from service\n");
	if(gfs_isDirExist(dir) != GFS_SUCCESS)
		return EDNOTVALID;
	
	//STEP 2: Check permission---------------------------------------------------------------------
	verbose("L:gfs_check_dir() : check permission\n");
	if(gfs_get_permission(dir->dir_name, dir->logical_loc, "x") != GFS_SUCCESS)
		return EPERMIT;
	
	return GFS_SUCCESS;
}
/**************************************************************************************************
**	REMOVE DIRECTORY
**************************************************************************************************/
int gfs_remove_dir(char *strGFSPath){
	verbose("L:gfs_remove_dir() : Params( gfs-path = %s)\n",  strGFSPath);
	//Define variable
	int iStatus;
	GFS_item itemList = NULL;
	GFS_dir dir = malloc(sizeof(GFS_dirD));
	
	//STEP 0: build dir handler ------------------------------------------------------------------
	verbose("L:gfs_remove_dir() : build dir handler\n");
	p_splitGFSPath(strGFSPath, dir->logical_loc, dir->dir_name);
    p_setErrorParam(dir->dir_name);
        
	//STEP 1: lock metadata------------------------------------------------------------------------
	verbose("L:gfs_remove_dir() : lock metadata\n");    
    iStatus = gfs_lockMetadata("xd", "wd", 0, dir->dir_name, dir->logical_loc);   
	if(iStatus <= GFS_FAILED)
		return iStatus;
	dir->pid = iStatus;
    
    //STEP 2: Cek Dir not empty ------------------------------------------------------------------
	verbose("L:gfs_remove_dir() : get file list\n");
	iStatus = gfs_getItemList(dir, &itemList);
    if(iStatus > 0)
		iStatus = EDNOTEMPTY;
    else if(iStatus == 0){
	
        //STEP 3: save matadata -----------------------------------------------------------------------
        verbose("L:gfs_remove_dir() : save metadata\n");
		strcpy(dir->mode, "d"); //delete mode
        iStatus = gfs_saveDirMetadata(dir);
        dir->process_status = iStatus;
        
    }
    //STEP 4: unlock matadata ----------------------------------------------------------------------
	verbose("L:gfs_remove_dir() : unlock metadata\n");
    gfs_unlockMetadata(dir->pid, 0, dir->dir_name, dir->logical_loc);
	    
	//STEP 5: Return if all step above success-----------------------------------------------------
	return iStatus;
}
/**************************************************************************************************
**	MOVE DIRECTORY
**************************************************************************************************/
int p_gfs_move_dir(char *strSrcGFSPath, char *strDstGFSPath){
	verbose("L:gfs_move_dir() : Params( src-gfs-path = %s, dst-gfs-path = %s)\n",  strSrcGFSPath, strDstGFSPath);
	//Define variable
	int iStatus;
	GFS_dir src_dir = malloc(sizeof(GFS_dirD));
	GFS_dir dst_dir = malloc(sizeof(GFS_dirD));
	
	//STEP 0: build dir handler ------------------------------------------------------------------
	verbose("L:gfs_move_dir() : build dir handler\n");
	p_splitGFSPath(strSrcGFSPath, src_dir->logical_loc, src_dir->dir_name);
	p_splitGFSPath(strDstGFSPath, dst_dir->logical_loc, dst_dir->dir_name);
    
    //STEP 1: lock metadata -----------------------------------------------------------------------
	verbose("L:gfs_move_dir() : lock metadata \n");
    iStatus = gfs_lockMetadata("xd", NULL, 0, src_dir->dir_name, src_dir->logical_loc);    
	if(iStatus <= GFS_FAILED){
        p_setErrorParam(src_dir->dir_name);
        return iStatus;
    }
    src_dir->pid = iStatus;
    iStatus = gfs_lockMetadata("xd", NULL, 1, dst_dir->dir_name, dst_dir->logical_loc);    
	if(iStatus <= GFS_FAILED){
        p_setErrorParam(dst_dir->dir_name);
        return iStatus;
    }
    dst_dir->pid = iStatus;
            
    //STEP 2: move metadata ------------------------------------------------------
    verbose("L:gfs_move_dir() : move metadata\n");
    strcpy(src_dir->mode, "d"); // delete mode
	strcpy(dst_dir->mode, "w"); // write mode
    iStatus = gfs_moveDirMetadata(src_dir, dst_dir);
    src_dir->process_status = iStatus;    
    dst_dir->process_status = iStatus;   
	    
    //STEP 5: unlock Metadata----------------------------------------------------------------------
	verbose("L:gfs_move_dir() : unlock Metadata\n");	
    gfs_unlockMetadata(src_dir->pid, 0, src_dir->dir_name, src_dir->logical_loc);
    if(dst_dir->process_status == GFS_SUCCESS)   
    	gfs_unlockMetadata(dst_dir->pid, 0, dst_dir->dir_name, dst_dir->logical_loc);
    else 
    	gfs_unlockMetadata(dst_dir->pid, 1, dst_dir->dir_name, dst_dir->logical_loc);
           
	//STEP 6: done if all step above done----------------------------------------------------------
	return iStatus;     
}

//									//
// ACCESS BOTH DIRECTORY & FOLDER	//
//									//
/**************************************************************************************************
**	DELETE RECURSIVE
**************************************************************************************************/
int gfs_remove_recursive(char *strGFSPath){
	verbose("L:gfs_remove_recursive() : Params( gfs-path = %s)\n",  strGFSPath);
	// Define Variable
	int iResult;
	GFS_dir dir = malloc(sizeof(GFS_dirD));
	p_splitGFSPath(strGFSPath, dir->logical_loc, dir->dir_name);
    
	
	//BASE 0: delete single file-------------------------------------------------------------------
    if(gfs_isDirExist(dir) != GFS_SUCCESS) {
    	verbose("L:gfs_remove_recursive() : delete file\n");    
    	iResult	= gfs_remove_file(strGFSPath);
    	if( iResult != GFS_SUCCESS){
    		if(iResult == EFNOTVALID)
				return EFDNOTVALID;
			else
				return iResult;
    	}
    	return GFS_SUCCESS;
    }
	
	//BASE 1: access directory---------------------------------------------------------------------
	verbose("L:gfs_remove_recursive() : access directory\n");
    GFS_item listItem = NULL;				
    if(gfs_getItemList(dir, &listItem) >0 ){// not-Empty Directory
    	GFS_item now=NULL;
    	if (listItem != NULL)
		{
			now = listItem;
			char strNextPath[MAX_PATH];
			while (now)
			{
				p_getFullPath(strGFSPath, now->item_name, strNextPath);
				gfs_remove_recursive(strNextPath);				
				now=now->next;
			}
		}
    }
    
    //BASE 2: remove directory---------------------------------------------------------------------
    verbose("L:gfs_remove_recursive() : delete directory\n");
    iResult =gfs_remove_dir(strGFSPath) ;
    if(iResult!= GFS_SUCCESS)	// already Empty Directory
		return iResult;
    
    //BASE 3: success delete-----------------------------------------------------------------------
    return GFS_SUCCESS;    
}
/**************************************************************************************************
**	MOVE RECURSIVE
**************************************************************************************************/
int gfs_move(char *strSrcGFSPath, char *strDstGFSPath){
	verbose("L:gfs_move() : Params( src-gfs-path = %s, dst-gfs-path = %s)\n",  strSrcGFSPath, strDstGFSPath);    
	// Define Variable
	int iResult;
	GFS_dir src_dir = malloc(sizeof(GFS_dirD));
	GFS_dir dst_dir = malloc(sizeof(GFS_dirD));
	p_splitGFSPath(strSrcGFSPath, src_dir->logical_loc, src_dir->dir_name);
	p_splitGFSPath(strDstGFSPath, dst_dir->logical_loc, dst_dir->dir_name);

   //STEP 0: check dir in Destination, if exist = just move but can, same as source-----------------
   verbose("L:gfs_move() : check dir in Destination\n");
   if(gfs_isDirExist(dst_dir) == GFS_SUCCESS){
	if(strcmp(strSrcGFSPath,strDstGFSPath) == 0)
		return EDEXIST;
	//renew*
	char strRenewPath[MAX_PATH];
	p_getFullPath(strDstGFSPath, src_dir->dir_name, strRenewPath);
	strcpy(strDstGFSPath, strRenewPath);
	p_splitGFSPath(strDstGFSPath, dst_dir->logical_loc, dst_dir->dir_name);
   }
    		
   //BASE 0: move single file----------------------------------------------------------------------
   if(gfs_isDirExist(src_dir) != GFS_SUCCESS){
    	verbose("L:gfs_move() : move file\n");
    	int iResult = p_gfs_move_file(strSrcGFSPath, strDstGFSPath);
    	if( iResult != GFS_SUCCESS){ 
    		if(iResult == EFNOTVALID)
				return EFDNOTVALID;
			else
				return iResult;
    	}
    	return GFS_SUCCESS;
    }
	
	//STEP 1: move directory-----------------------------------------------------------------------	
	verbose("L:gfs_move() : move directory\n");
	iResult = p_gfs_move_dir(strSrcGFSPath, strDstGFSPath) ; 
	if(iResult!= GFS_SUCCESS)
		return iResult;
			
	//BASE 1: access directory---------------------------------------------------------------------
	verbose("L:gfs_move() : access directory\n");
    	GFS_item listItem = NULL;    
    	if(gfs_getItemList(src_dir, &listItem) > 0){	// not-Empty Directory
    		
		GFS_item now=NULL;
    	if (listItem != NULL)
		{
			now = listItem;
			char strNextSrcPath[MAX_PATH];
			char strNextDstPath[MAX_PATH];
			while (now != NULL)
			{
				p_getFullPath(strSrcGFSPath, now->item_name, strNextSrcPath);
				p_getFullPath(strDstGFSPath, now->item_name, strNextDstPath);
				gfs_move(strNextSrcPath, strNextDstPath);							
				now=now->next;
			}
		}
    } 
    
	//BASE 2: Remove directory-----------------------------------------------------------------------	
	verbose("L:gfs_move() : delete directory\n");
    iResult =gfs_remove_dir(strSrcGFSPath) ; // already Empty Directory
    if(iResult!= GFS_SUCCESS)
		return iResult;

    //BASE 3: success move-------------------------------------------------------------------------
    return GFS_SUCCESS;    
}


//					//
// 	CONFIGURATIOn	//
//					//
/**************************************************************************************************
**	GET ERROR STRING
**************************************************************************************************/
char *gfs_get_error(int iStatus){
	verbose("L:gfs_get_error() : Params( error-number = %d)\n",  iStatus);
	char *resultPtr;
	char strResult[100];
	
	switch(iStatus){
		// Trouble with connection
		case -1 :
			strcpy(strResult, "Trouble with GFS connection, call your GFS administrator for help! \0");
			break;
		// Trouble with service
		case -2 :
			strcpy(strResult, "Trouble with GFS services, call your GFS administrator for help! \0");
			break;
		// Trouble with file
		case -11 : 
			strcpy(strResult, "File '");
			strcat(strResult, strErrorParam_g);
			strcat(strResult, "' is not valid. \0");
			break;
		case -12 : 
			strcpy(strResult, "File '");
			strcat(strResult, strErrorParam_g);
			strcat(strResult, "' already exist. \0");
			break;
		// Trouble with directory
		case -21 : 
			strcpy(strResult, "Directory '");
			strcat(strResult, strErrorParam_g);
			strcat(strResult, "' is not valid. \0");
			break;
		case -22 : 
			strcpy(strResult, "Directory '");
			strcat(strResult, strErrorParam_g);
			strcat(strResult, "' already exist. \0");
			break;
		case -23 : 
			strcpy(strResult, "Directory '");
			strcat(strResult, strErrorParam_g);
			strcat(strResult, "' is not empty. \0");
			break;
		// Trouble with file or directory 
		case -31 : 
			strcpy(strResult, "File or directory '");
			strcat(strResult, strErrorParam_g);
			strcat(strResult, "' is not valid. \0");
			break;
		// Trouble with permission & locking
		case -41 : 
			strcpy(strResult, "Permission denied \0");
			break;
		case -42 : 
			strcpy(strResult, "Item is accessed by another process \0");
			break;
		default :
			return '\0';	
	}
	
	resultPtr = strResult;
	return resultPtr;
}
/**************************************************************************************************
**	GET SHOW SERVER
**************************************************************************************************/
int gfs_get_server(GFS_server *headPtr){
	verbose("L:gfs_get_server() : Params( reference to list serve\n");
	
	//STEP 1: get available server-----------------------------------------------------------------
  	verbose("L:gfs_get_server() : get available server\n");
  	int iResult = gfs_get_all_server(headPtr);
	if(iResult != GFS_SUCCESS)
		return iResult;
	
	//STEP 2: all done-----------------------------------------------------------------------------
	return GFS_SUCCESS;
}
/**************************************************************************************************
**	GET CHANGE MODE
**************************************************************************************************/
int gfs_change_mode(char *strGFSPath, char *mode){
	verbose("L:gfs_change_mode() : Params( gfs-path = %s, mode= %s)\n",  strGFSPath, mode);
    //Define Variable    
    char strItemName[strlen(strGFSPath)];
    char strGFSLoc[strlen(strGFSPath)];
	p_splitGFSPath(strGFSPath, strGFSLoc, strItemName);
  	p_setErrorParam(strItemName);
	
	//STEP 1: change mode -------------------------------------------------------------------------
  	verbose("L:gfs_change_mode() : change mode \n");
	int iResult = gfs_change_permission(strItemName, strGFSLoc, mode);
	if(iResult != GFS_SUCCESS)
		return iResult;
	
	//STEP 2: all done-----------------------------------------------------------------------------
	return GFS_SUCCESS;
}
/**************************************************************************************************
**	GET SUPER USER ON
**************************************************************************************************/
int gfs_SU_on(char *strPassword){
	int result = gfs_superuser_check(strPassword);

	if(result != GFS_SUCCESS)
		return result;

	GFS_SUPER_USER = GFS_SUCCESS;
	return GFS_SUCCESS;
}
/**************************************************************************************************
**	GET SUPER USER OFF
**************************************************************************************************/
int gfs_SU_off(){
	GFS_SUPER_USER = GFS_FAILED;
	return GFS_SUCCESS;
}
/**************************************************************************************************
**	GET SUPER USER CHANGE PASSWORD
**************************************************************************************************/
int gfs_SU_change_pwd(char *strOldPassword, char *strNewPassword){
	int result = gfs_superuser_chpwd(strOldPassword, strNewPassword);

	if(result != GFS_SUCCESS)
		return result;

	return GFS_SUCCESS;
}

// ADDITIONAL FUNCTION : PRIVATE//
/************************
*	GET FILE SIZE	*
************************/
int p_getFileSize(FILE *input){
    int fileSizeBytes;
    fseek(input, 0, SEEK_END);
    fileSizeBytes = ftell(input);
    fseek(input, 0, SEEK_SET);
    return fileSizeBytes;
}
/************************
*	GENERATE FULL PATH 	*
************************/
void p_getFullPath(char *strLocation, char *strName, char *strGFSPath_ref){
	
	strcpy(strGFSPath_ref, strLocation);
	if(strcmp(strGFSPath_ref,"/")) // not-root
		strcat(strGFSPath_ref, "/");
	strcat(strGFSPath_ref, strName);
}
/****************************
*	SET ERROR PARAM GLOBAL 	*
*****************************/
void p_setErrorParam(char *strParam){
    strcpy(strErrorParam_g, strParam);
}
/************************
*	SLIT GFS PATH   	*
************************/
void p_splitGFSPath(char *strGFSPath, char *strGFSLoc_ref, char *strItemName_ref){
	char *pathPtr;		
    
	strcpy(strGFSLoc_ref,strGFSPath);
	pathPtr = strrchr(strGFSLoc_ref, '/');
	if (pathPtr == NULL){	//if no root path '/' = from root
		strcpy(strGFSLoc_ref, "/");
		strcpy(strItemName_ref, strGFSPath);
	}
	else if (pathPtr-strGFSLoc_ref==0 && strlen(strGFSLoc_ref)==1) 	//if '/' is root
		strcpy(strItemName_ref, "");
	else {
		strcpy(strItemName_ref,pathPtr+1);
		if(pathPtr-strGFSLoc_ref == 0)	    //avoid delete root	
			pathPtr[1] = 0x00;
		else
			pathPtr[0] = 0x00;
	}
}
/************************
*	SEEK BUFFER BYTE	*
************************/
int p_seek_buffer(globus_byte_t *from_buffer,  int iFrom, int iTo,  globus_byte_t *to_buffer){
	int i;
	int iSize = 0;
	for(i = iFrom; i < iTo && i <= size_g; i++){
		to_buffer[i] = from_buffer[i];
		iFrom = iFrom +1;
		iSize = iSize +1;
	}
	return iSize;
}
// GLOBUS GRID-FTP ADDITIONAL FUNCTION : PRIVATE //
/************************
*   GLOBUS PREPARATION	*
************************/
int p_pre_globus(globus_action ga){
	//verbose("L:pre_globus()--Params(physical_path = %s)\n", physical_path->url);
	int 	result;
	
	/*********************************************************************
    	* STEP 1: Activate Module and do miscellaneious initializations
    	* The mutex and cond are theoretically optional, but highly recommended
    	* because they will make the code work correctly in a threaded build.
    	*********************************************************************/
	//verbose("L:pre_globus()--Globus module activated\n");
    	globus_mutex_init(&ga->lock, GLOBUS_NULL);
    	globus_cond_init(&ga->cond, GLOBUS_NULL);

	/*********************************************************************
    	* STEP 2: Handle Setup; The handle_attr is only necessary if you want
    	* to use a handle_attr (currently that means plug-ins or channel
    	* caching).  We recommend you do use channel caching for performance
    	* reasons and that is what is shown here.  If you choose not to use
    	* a handle_attr, just pass NULL to the handle_init call.
    	*********************************************************************/
	//verbose("L:pre_globus()--Globus handle set\n");
	globus_ftp_client_handle_init(&ga->handle, GLOBUS_NULL);
	
	//STEP 3: return if all step above success**
	return GFS_SUCCESS;
}
/************************
*   GLOBUS END		*
************************/
int p_post_globus(globus_action ga){	
	
	/**********************************************************************
    	* Since the reference count is zero, all callbacks have completed.
    	* The transfer is now completely finished (both control channel and
    	* data channel).  Now, Clean up and go home
    	**********************************************************************/	
   	//verbose("L:post_globus()--Globus Deactivated and Cleanup\n");
    	globus_ftp_client_handle_destroy(&ga->handle);

    	//STEP 0: return if all step above success**	
   	return GFS_SUCCESS;
}
/************************
*	DATA CALLBACK	*
************************/
/* data_callback : 
 * read or write operation in the FTP Client library is asynchronous.
 * A callback of this type is passed to such data  operation function calls.
 * It is called when the user supplied buffer has been successfully transferred to the kernel.
 * Note: That does not mean it has been successfully transmitted,
 * instead it just reads the next block of data and calls register_write/register_read again.
 */
static void p_write_data_callback(void * user_arg, 
							globus_ftp_client_handle_t * handle, globus_object_t * err, 
							globus_byte_t * buffer, globus_size_t length, 
							globus_off_t offset, globus_bool_t eof) {
								
	//STEP 1: ERROR checking*
	if (err) {
        verbose("write_data_callback()--|X| %s\n", globus_object_printable_to_string(err));
        return;
   	}
   	
	//STEP 2: Register the data buffer to handle a part of the FTP data transfer slight recursive thing going on here*
	if(!eof){ // actually always eof (in globus_partial_put case)
	     	// This code actually can't be reach
		//**/printf("realy?");
		globus_ftp_client_register_write(handle, buffer, length, offset + length, GLOBUS_TRUE, p_write_data_callback, (void *) user_arg);
	}
   	return;
    
} 
static void p_read_data_callback(void * user_arg, 
							globus_ftp_client_handle_t * handle,globus_object_t * err, 
							globus_byte_t * buffer, globus_size_t length, 
							globus_off_t offset, globus_bool_t eof) {
	
	//STEP 1: ERROR checking*
    if (err) {
        verbose("read_data_callback()--|X| %s\n",globus_object_printable_to_string(err));
        return;
    }
    
    //STEP 2: Register the data buffer to handle a part of the FTP data transfer slight recursive thing going on here*
    if(!eof){ // actually always eof (in globus_partial_get case, its buffer exactly in its size (offset_end - offset))
     	// This code actually can't be reach
        //**/printf("realy?");
        globus_ftp_client_register_read(handle, buffer, length, p_read_data_callback, (void *)user_arg);
    }
   	return;
}  
/************************
*	CALLBACK FUNCTION	*
************************/
/* callback_function : 
 * A pointer to this function is passed to operation function calls to know when the operation is complete.
 * It is called when the transfer is completely finished, i.e. both the data channel and control channel exchange. 
 * Here it simply sets a global variable (done) to true so the main program will exit the while loop.
 */
void p_callback_function(	void * user_arg, 
						globus_ftp_client_handle_t * handle, 
						globus_object_t * err_str)  {
	
	//STEP 1: ERROR checking*
     	if(err_str) {
          verbose("callback_function()--|X| %s\n", globus_object_printable_to_string(err_str));
     	}
	 
	//STEP 2: EXECUTION*
	globus_action ga = (globus_action)user_arg;
     	// block until the mutual exclusion lock ,mutex is acquired*
     	globus_mutex_lock(&ga->lock);
     		ga->done = GLOBUS_TRUE;

     		/* signal the specifide condition ,waking up one thread that is suspended on this condition .
      		* If no thread are suspended on this condition ,this call will have no effect.
      		*/
     		globus_cond_signal(&ga->cond);

     		/*Unlock the mutual exclusion lock ,mutex , enabling another thread to acquire the mutex */
     	globus_mutex_unlock(&ga->lock);
     
	return;
}
/*
void p_exist_callback_function(	void * user_arg, 
						globus_ftp_client_handle_t * handle, 
						globus_object_t * err_str)  {
	
	 GFS_physical_path physical_path = (GFS_physical_path)user_arg;
	 
	//STEP 1: ERROR checking*
     if(err_str) {
        verbose("exist_callback_function()--|X| %s\n", globus_object_printable_to_string(err_str));
     }
     else{
     	exists_g = GLOBUS_TRUE;
     }
     
     done_g = GLOBUS_TRUE;
}
*/
